package com.iisigroup.http.test;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.KeyManagementException; 
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.SSLContext; 
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager; 
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair; 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse; 
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 

/**
 * Hello world!
 * 參考https://hc.apache.org/httpcomponents-client-4.3.x/quickstart.html
 * 而做出來的測試程式碼
 */
public class App5 {
    private final static int num =50;
    private final static Logger LOGGER = LoggerFactory.getLogger(App5.class);
    private final static String captchaKeyRegExpress = ".*<input\\D*type=\"hidden\"\\D*name=\"captchaKey\"\\D*value=\"(.*)\".*\\/>.*";
    private final static String captchaKeycheckRegExpress = ".*<input\\D*type=\"hidden\"\\D*name=\"lastCaptchaKey\"\\D*value=\"(.*)\".*\\/>.*";
    private  static String personIdFileName = null; 
    
    private String extractLastCaptchaKey(final String content){
    	final	Matcher matcher = Pattern.compile(captchaKeycheckRegExpress).matcher(content);
    	if(matcher.find()){
    		return matcher.group(matcher.groupCount());
    	}
    	return null;
    }
    private String extractCaptchaKey(final String content){
    	final	Matcher matcher = Pattern.compile(captchaKeyRegExpress).matcher(content);
    	if(matcher.find()){
    		return matcher.group(matcher.groupCount());
    	}
    	return null;
    }
    public static void main(String[] args) throws Exception {    	
        LOGGER.info("App5.................");
        if(ArrayUtils.isNotEmpty(args)){
        	File personIdFile = new File(args[0]);
        	if(personIdFile==null && !personIdFile.exists()){
        		System.out.println(String.format("%s does not exist ! ", args[0]));        		
        	}else {
        		System.out.println(String.format("use file %s ! ", args[0]));      
        		personIdFileName = args[0];
        	}
        }
//        		while (true) {
//        ResponseResult rr = new App5().testMethod();
//        LOGGER.info("average time: {}",String.valueOf((rr.getAverageTime())/ 1.0e3)) ;
//        LOGGER.info("非預期結果比數 :{}",rr.getWrongValidationRec().size()) ;
//        LOGGER.info("-----------------------") ;
        
//        		}
        		new App5().multiProcess();
    } 
    public String getRandomPersonId(List<String> personIdList ){
    	String tmpPersonId = getRandomPersonId();
    	if(StringUtils.isNotBlank(tmpPersonId)){
    		return tmpPersonId;
    	}
    	
    	String personId = personIdList.get(RandomUtils.nextInt(personIdList.size()));
    	if(StringUtils.isBlank(personId)){
    		return personIdList.get(RandomUtils.nextInt(personIdList.size()));
    	}else{
    		return personId;
    	}
    }
    private static String getRandomPersonId(){
		String result =null;
		RandomAccessFile raf = null;
		try {
			// create a new RandomAccessFile with filename test
			raf = new RandomAccessFile(new File(
					personIdFileName != null ? personIdFileName
							: "/home/weblogic/Desktop/person2.csv"), "r");
			int lines =(int) raf.length()/11;		
			int randomInt = ThreadLocalRandom.current().nextInt(lines);			
			raf.seek(randomInt * 11);
			String pId = raf.readLine();
			if (pId.length() == 10) {
				result = pId;
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}finally{
			if (raf != null) {
				try {
					raf.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return result ;
	}
    private void getResponseByGet(final  org.apache.http.client.HttpClient httpclient , final String... urls){
    	for(String url : urls){
    		 final HttpGet httpGet = new HttpGet(url);
			try {
				final HttpResponse response1 = httpclient.execute(httpGet);
				final HttpEntity entity1 = response1.getEntity();
	            final String initContent = EntityUtils.toString(entity1);
	            EntityUtils.consume(entity1);
			} catch (ClientProtocolException e) {
				LOGGER.error(e.getMessage(),e);				
			} catch (IOException e) {
				LOGGER.error(e.getMessage(),e);
			}
            
    	}
    }
     public List<String> urlResources(final String ip){
    	 List<String> result = new ArrayList<String>();
    	 result.add( String.format("https://%s/js/jquery-easyui-1.3.1/jquery-1.7.2.min.js", ip));
    	 result.add( String.format("https://%s/js/jquery-easyui-1.3.1/jquery.easyui.min.js", ip));
    	 result.add( String.format("https://%s/js/jquery-easyui-1.3.1/locale/easyui-lang-zh_TW.js", ip));
    	 result.add( String.format("https://%s/js/jquery-easyui-1.3.1/themes/default/easyui.css", ip));
    	 result.add( String.format("https://%s/js/jquery-easyui-1.3.1/themes/icon.css", ip));
    	 result.add( String.format("https://%s/js/swUtils.js", ip));
    	 result.add( String.format("https://%s/js/blockui/jquery.blockUI.js", ip));
    	 result.add( String.format("https://%s/images/logo.swf", ip));
    	
    	 return result;    	
     }
    public ResponseResult testMethod() throws ClientProtocolException, IOException {
    	String soundTemplate = "https://%s/swvote/captcha/sound?CAPTCHA_KEY=%s&lan=tw&time=%s";
    	String imageTemplate = "https://%s/swvote/captcha/image?CAPTCHA_KEY=%s&lan=tw&time=%s";
//    	final String ip = "10.20.24.1";
    	final String ip = "117.56.68.150";
    	final String validation4WrongBirthDate = "如您於本網站查無資料，請洽您戶籍所在地鄉（鎮、市、區）公所洽詢或申請更";
    	final String validationRight = "103年地方公職人員選舉選舉人資格查詢結果";
        final long times = 2l;
        final ResponseResult result = new ResponseResult();
        final List<String> personIdList = getPersonIdList();
        long totalTime = 0l;
        for (int c = 0; c < times; c++) {
        	try {
                Thread.sleep(RandomUtils.nextInt(100));
            } catch (final InterruptedException e) {
                LOGGER.error(e.getMessage(), e);
            }
        	boolean isRight = (c%2==0);
        	final long startTime = System.currentTimeMillis();
            final CloseableHttpClient httpclient = getHttpClient();
            //10.20.24.1
            //117.56.68.150
            final HttpGet httpGet = new HttpGet("https://"+ip+"/swvote/voteReissueController.do?toMain&t=" + startTime);
            final CloseableHttpResponse response1 = httpclient.execute(httpGet);
            final HttpEntity entity1 = response1.getEntity();
            final String initContent = EntityUtils.toString(entity1);
            
            final String extractCaptchaKey =  extractCaptchaKey(initContent);
            final String imageUrl = String.format(imageTemplate, ip,extractCaptchaKey,new Date().getTime());
            final String soundUrl = String.format(soundTemplate, ip,extractCaptchaKey,new Date().getTime());
            final List<String> urlList = urlResources(ip);
            urlList.add(imageUrl);
            urlList.add(soundUrl);
            getResponseByGet(httpclient, urlList.toArray(new String[]{}));
            
            
            //10.20.24.1
            //117.56.68.150
            final HttpPost httpPost = new HttpPost("https://"+ip+"/swvote/voteReissueController.do?toResult&t="
                    + startTime);
            final List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            final String personId = getRandomPersonId(personIdList);
            nvps.add(new BasicNameValuePair("idnum",personId));
            if(isRight){
            	nvps.add(new BasicNameValuePair("year", "10"));
                nvps.add(new BasicNameValuePair("mon", "10"));
                nvps.add(new BasicNameValuePair("day", "10"));
            }else{
            	nvps.add(new BasicNameValuePair("year","11"));
                nvps.add(new BasicNameValuePair("mon","11"));
                nvps.add(new BasicNameValuePair("day","11"));
            }
            nvps.add(new BasicNameValuePair("captchaKey", extractCaptchaKey));
            nvps.add(new BasicNameValuePair("captchaInput", "#^7ii")); 
            
          //10.20.24.1
            //117.56.68.150
            httpPost.addHeader("Referer", "https://"+ip+"/swvote/voteReissueController.do?toMain");
            httpPost.addHeader("Cache-Control", "no-cache");
            String host = String.format("%s.%s.%s.%s", RandomUtils.nextInt(255), RandomUtils.nextInt(255), RandomUtils.nextInt(255), RandomUtils.nextInt(255));
            httpPost.addHeader("Host", host);
            httpPost.addHeader("User-Agent", "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A498b Safari/419.3");
            httpPost.setEntity(new UrlEncodedFormEntity(nvps)); 
            CloseableHttpResponse response2 = null;

            try {
            	response2 = httpclient.execute(httpPost); 
            	
                final String responseStatus = response2.getStatusLine().toString().intern();
                result.getListResult().add(responseStatus.toString().intern());
                //LOGGER.info(responseStatus);
                final HttpEntity entity2 = response2.getEntity();
                final String content = EntityUtils.toString(entity2);
                final String lastCaptchaKey =  extractLastCaptchaKey(content);
                if(StringUtils.equalsIgnoreCase(extractCaptchaKey, lastCaptchaKey) && isRight && StringUtils.contains(content, validationRight)){
//                	result.setValidation(true);
                	//註解掉的原因試在於這一組result是多次查詢而來的結果...萬一第一次驗證錯誤...最後一次驗證成功...最後在回收結果時被蓋住結果....
                }else if(StringUtils.equalsIgnoreCase(extractCaptchaKey, lastCaptchaKey) && !isRight && StringUtils.contains(content, validation4WrongBirthDate)) {
//                	result.setValidation(true);
                }else {
                	result.setValidation(false);
                	Record record = new Record();
                	record.setPersonId(personId);
                	record.setContent1st(initContent);
                	record.setContent2nd(content);                	
                	result.getWrongValidationRec().add(record);
                }
                final Header[] allHeaders = response2.getAllHeaders();
//                for(Header aHeader : allHeaders){
//                	LOGGER.info("name:{} ,value:{}" ,aHeader.getName(),aHeader.getValue());                	
//                }
               
                EntityUtils.consume(entity2);
                final List<String> urlList2 = urlResources(ip);
                getResponseByGet(httpclient, urlList2.toArray(new String[]{}));
            } catch (final Exception e) {
                System.out.println("ERR:" + e.toString());
            } finally {
            	if(response2!=null){
            		response2.close();
            	}
            }
            final long intervalTime = System.currentTimeMillis() - startTime;
            totalTime += intervalTime;
            httpclient.close();
            LOGGER.info("{}",intervalTime);
            
        }
        result.setAverageTime(totalTime / times);

        //如果要測試戶籍員的反應...則要把httpclient.close();註解掉,這樣才會使得stuck thread 不會消失
        return result;
    }

    public List<String> getPersonIdList() {
        final InputStream is = getClass().getResourceAsStream("person.csv");
        List<String> result =null;
        try {
            result = IOUtils.readLines(is);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        }finally{
            if(is !=null ){
                try {
                    is .close();
                } catch (IOException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
        }
        return result;
    }
    public void multiProcess() {
        List<Future<ResponseResult>> data = null;
        final List<HttpTask> tasks = new ArrayList<HttpTask>();
        for (int j = 0; j < 100000; ++j) {
            tasks.add(new HttpTask());
        }
        //		ExecutorService service = Executors.newCachedThreadPool();
        ExecutorService service = Executors.newFixedThreadPool(num);

        try {
            data = service.invokeAll(tasks, 10000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            service.shutdown();
        }
        final List<ResponseResult> finalData = new ArrayList<ResponseResult>();
        if (data != null && data.size() > 0) {
            for (Future<ResponseResult> futureUnit : data) {
                try {
                	ResponseResult rr =  futureUnit.get();
                	finalData.add(rr);
                } catch (Exception e) {
                	LOGGER.error(e.getMessage(), e);
                }
            }
        }
        int wrongCount =0;
        long total =0l;
        int countResponse =0;
    	final List<Record> record  =new ArrayList<Record>(); 
        for(ResponseResult rr  : finalData){
        	total+=rr.getAverageTime();
        	countResponse +=	rr.getListResult().size();
        	if(!rr.isValidation()){
        		wrongCount++;
        		final List<Record> list = rr.getWrongValidationRec();
        		record.addAll(list);
        	}
        }
        long num = (long)finalData.size();
        LOGGER.info("average time: {}",String.valueOf((total/num)/ 1.0e3)) ;
        LOGGER.info("執行筆數: {}",countResponse) ;
        LOGGER.info("非預期結果比數 :{}",wrongCount) ;
        LOGGER.info("-----------------------") ;
        for(Record rr :record){
			LOGGER.info("persojn_id: {}", rr.getPersonId());
			LOGGER.info("第一次response \r\n: {}", rr.getContent1st());
			LOGGER.info("第二次response \r\n: {}", rr.getContent2nd());
			LOGGER.info("-----------------------");
        }
    }

    public class HttpTask implements Callable<ResponseResult> {
        @Override
        public ResponseResult call() throws Exception {
            return testMethod();
        }
    }
    public CloseableHttpClient getHttpClient() {
        try {
			final HttpHost proxy = new HttpHost("172.16.0.14", 3128);
			final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(
					proxy);
			final SSLConnectionSocketFactory sslsf = getSSLConnectionSocketFactory();
//			final CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf) .build();
               
			final CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).setRoutePlanner(routePlanner).build();
             
            return httpclient;
        } catch (Exception e) {
            e.printStackTrace();
            return HttpClients.createDefault();
        }
    }

    public SSLConnectionSocketFactory getSSLConnectionSocketFactory() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, KeyManagementException {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            }
        } };
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new TrustingHostnameVerifier());
        return sslsf;
    }

    private static final class TrustingHostnameVerifier implements X509HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
        @Override
        public void verify(String host, SSLSocket ssl) throws IOException {
        }
        @Override
        public void verify(String host, X509Certificate cert) throws SSLException {
        }
        @Override
        public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
        }
    } 
}
